package com.wh.common.annotation.aspect;

import cn.hutool.json.JSONUtil;
import com.google.common.collect.Maps;
import com.wh.common.annotation.ResultLog;
import com.wh.common.log.RsLogBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * @author luweije
 */

@Aspect
@Component
@Slf4j
public class ResultLogAspect {

    private static final Logger RESULT_LOGGER = LoggerFactory.getLogger("RESULT_LOG");

    @Value("${spring.application.name:unknown}")
    private String appName;

    @Pointcut("@annotation(com.wh.common.annotation.ResultLog)")
    public void pointCut() {}

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Object result = null;
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        ResultLog resultLog = method.getAnnotation(ResultLog.class);
        if (resultLog == null) {
            return point.proceed();
        }
        RsLogBean rsLogBean = new RsLogBean();
        boolean success = true;
        long start = System.currentTimeMillis();
        try {
            result = point.proceed();
        } catch (Throwable e) {
            success = false;
            throw e;
        } finally {
            try {
                rsLogBean.setAppName(appName);
                rsLogBean.setName(resultLog.name());
                rsLogBean.setMethodTypeEnum(resultLog.methodType());
                if (StringUtils.isBlank(rsLogBean.getName())) {
                    rsLogBean.setName(point.getSignature().getDeclaringTypeName() + "." + method.getName());
                }
                rsLogBean.setParam(genParam(point));
                rsLogBean.setResult(result);
                rsLogBean.setSuccess(success);
                rsLogBean.setRt(System.currentTimeMillis() - start);
                RESULT_LOGGER.info(JSONUtil.toJsonStr(rsLogBean));
            } catch (Exception e) {
                log.error("日志注解获取数据异常 rsLogBean={}", JSONUtil.toJsonStr(rsLogBean), e);
            }
        }
        return result;
    }

    private Map<String, Object> genParam(ProceedingJoinPoint point) {
        Object[] args = point.getArgs();
        String[] parameterNames = ((MethodSignature) point.getSignature()).getParameterNames();
        Map<String, Object> paramMap = Maps.newHashMap();
        for (int i = 0; i < parameterNames.length; i++) {
            paramMap.put(parameterNames[i], args[i]);
        }
        return paramMap;
    }

}
